package com.liujit.web.handler;

import com.liujit.common.domain.result.ErrorResult;
import com.liujit.common.enums.ResultCode;
import com.liujit.common.exception.BusinessException;
import com.liujit.validator.domain.ErrorMessage;
import com.liujit.validator.domain.ValidResult;
import com.liujit.validator.utils.ValidHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.List;

/**
 * @description: 全局异常处理类
 * @author: liujun
 * @create: 2021/1/4 10:20 上午
 **/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 违反约束异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<ErrorResult> handleConstraintViolationException(ConstraintViolationException e,
            HttpServletRequest request) {
        log.info("ConstraintViolationException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(),
                e.getClass(), e.getMessage());
        List<ErrorMessage> errorMessageList = ValidHelper.convertConstraintViolationSet(e.getConstraintViolations());
        String msg = new ValidResult(errorMessageList).getErrors();
        ErrorResult errorResult = ErrorResult.failure(ResultCode.PARAM_INVALID, e, HttpStatus.BAD_REQUEST, msg);
        return ResponseEntity.status(HttpStatus.valueOf(errorResult.getStatus())).body(errorResult);
    }

    /**
     * 处理验证参数封装错误时异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<ErrorResult> handleConstraintViolationException(HttpMessageNotReadableException e,
            HttpServletRequest request) {
        log.info("HttpMessageNotReadableException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(),
                e.getClass(), e.getMessage());
        ErrorResult errorResult = ErrorResult.failure(ResultCode.PARAM_TYPE_ERROR, e, HttpStatus.BAD_REQUEST);
        return ResponseEntity.status(HttpStatus.valueOf(errorResult.getStatus())).body(errorResult);
    }

    /**
     * 处理参数绑定时异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public ResponseEntity<ErrorResult> handleBindException(BindException e, HttpServletRequest request) {
        log.info("BindException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(),
                e.getMessage());
        List<ErrorMessage> errorMessageList = ValidHelper.convertBindingResult(e.getBindingResult());
        String msg = new ValidResult(errorMessageList).getErrors();
        ErrorResult errorResult = ErrorResult.failure(ResultCode.PARAM_INVALID, e, HttpStatus.BAD_REQUEST, msg);
        return ResponseEntity.status(HttpStatus.valueOf(errorResult.getStatus())).body(errorResult);
    }

    /**
     * 处理使用@Validated注解时，参数验证错误异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResult> handleMethodArgumentNotValidException(MethodArgumentNotValidException e,
            HttpServletRequest request) {
        log.info("MethodArgumentNotValidException , uri:{}, exception:{}, caused by: {}", request.getRequestURI(),
                e.getClass(), e.getMessage());
        List<ErrorMessage> errorMessageList = ValidHelper.convertBindingResult(e.getBindingResult());
        String msg = new ValidResult(errorMessageList).getErrors();
        ErrorResult errorResult = ErrorResult.failure(ResultCode.PARAM_INVALID, e, HttpStatus.BAD_REQUEST, msg);
        return ResponseEntity.status(HttpStatus.valueOf(errorResult.getStatus())).body(errorResult);
    }

    /**
     * 处理通用自定义业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResult> handleBusinessException(BusinessException e, HttpServletRequest request) {
        log.info("BusinessException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(),
                e.getMessage());
        ErrorResult errorResult = ErrorResult.failure(e);
        return ResponseEntity.status(HttpStatus.valueOf(errorResult.getStatus())).body(errorResult);
    }

    /**
     * 处理未预测到的其他错误
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Throwable.class)
    public ResponseEntity<ErrorResult> handleThrowable(Throwable e, HttpServletRequest request) {
        log.error("Throwable start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(),
                e.getMessage());
        ErrorResult errorResult = ErrorResult
                .failure(ResultCode.SYSTEM_INNER_ERROR, e, HttpStatus.INTERNAL_SERVER_ERROR);
        return ResponseEntity.status(HttpStatus.valueOf(errorResult.getStatus())).body(errorResult);
    }
}
